//===-- PluginManager.cpp ---------------------------------------*- C++ -*-===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//

#include "lldb/Core/PluginManager.h"

// C Includes
// C++ Includes
#include <climits>
#include <string>
#include <vector>

// Other libraries and framework includes
#include "llvm/ADT/StringRef.h"
#include "llvm/Support/DynamicLibrary.h"

// Project includes
#include "lldb/Core/Debugger.h"
#include "lldb/Core/Error.h"
#include "lldb/Host/FileSpec.h"
#include "lldb/Host/Host.h"
#include "lldb/Host/HostInfo.h"
#include "lldb/Host/Mutex.h"
#include "lldb/Interpreter/OptionValueProperties.h"

using namespace lldb;
using namespace lldb_private;

enum PluginAction
{
    ePluginRegisterInstance,
    ePluginUnregisterInstance,
    ePluginGetInstanceAtIndex
};

typedef bool (*PluginInitCallback)();
typedef void (*PluginTermCallback)();

struct PluginInfo
{
    PluginInfo()
        : plugin_init_callback(nullptr), plugin_term_callback(nullptr)
    {
    }

    llvm::sys::DynamicLibrary library;
    PluginInitCallback plugin_init_callback;
    PluginTermCallback plugin_term_callback;
};

typedef std::map<FileSpec, PluginInfo> PluginTerminateMap;

static Mutex &
GetPluginMapMutex ()
{
    static Mutex g_plugin_map_mutex (Mutex::eMutexTypeRecursive);
    return g_plugin_map_mutex;
}

static PluginTerminateMap &
GetPluginMap ()
{
    static PluginTerminateMap g_plugin_map;
    return g_plugin_map;
}

static bool
PluginIsLoaded (const FileSpec &plugin_file_spec)
{
    Mutex::Locker locker (GetPluginMapMutex ());
    PluginTerminateMap &plugin_map = GetPluginMap ();
    return plugin_map.find (plugin_file_spec) != plugin_map.end();
}
    
static void
SetPluginInfo (const FileSpec &plugin_file_spec, const PluginInfo &plugin_info)
{
    Mutex::Locker locker (GetPluginMapMutex ());
    PluginTerminateMap &plugin_map = GetPluginMap ();
    assert (plugin_map.find (plugin_file_spec) == plugin_map.end());
    plugin_map[plugin_file_spec] = plugin_info;
}

template <typename FPtrTy>
static FPtrTy
CastToFPtr (void *VPtr)
{
    return reinterpret_cast<FPtrTy>(reinterpret_cast<intptr_t>(VPtr));
}

static FileSpec::EnumerateDirectoryResult 
LoadPluginCallback(void *baton,
                   FileSpec::FileType file_type,
                   const FileSpec &file_spec)
{
//    PluginManager *plugin_manager = (PluginManager *)baton;
    Error error;
    
    // If we have a regular file, a symbolic link or unknown file type, try
    // and process the file. We must handle unknown as sometimes the directory 
    // enumeration might be enumerating a file system that doesn't have correct
    // file type information.
    if (file_type == FileSpec::eFileTypeRegular         ||
        file_type == FileSpec::eFileTypeSymbolicLink    ||
        file_type == FileSpec::eFileTypeUnknown          )
    {
        FileSpec plugin_file_spec (file_spec);
        plugin_file_spec.ResolvePath();
        
        if (PluginIsLoaded (plugin_file_spec))
            return FileSpec::eEnumerateDirectoryResultNext;
        else
        {
            PluginInfo plugin_info;

            std::string pluginLoadError;
            plugin_info.library = llvm::sys::DynamicLibrary::getPermanentLibrary (plugin_file_spec.GetPath().c_str(), &pluginLoadError);
            if (plugin_info.library.isValid())
            {
                bool success = false;
                plugin_info.plugin_init_callback =
                    CastToFPtr<PluginInitCallback>(plugin_info.library.getAddressOfSymbol("LLDBPluginInitialize"));
                if (plugin_info.plugin_init_callback)
                {
                    // Call the plug-in "bool LLDBPluginInitialize(void)" function
                    success = plugin_info.plugin_init_callback();
                }

                if (success)
                {
                    // It is ok for the "LLDBPluginTerminate" symbol to be nullptr
                    plugin_info.plugin_term_callback =
                        CastToFPtr<PluginTermCallback>(plugin_info.library.getAddressOfSymbol("LLDBPluginTerminate"));
                }
                else 
                {
                    // The initialize function returned FALSE which means the plug-in might not be
                    // compatible, or might be too new or too old, or might not want to run on this
                    // machine.  Set it to a default-constructed instance to invalidate it.
                    plugin_info = PluginInfo();
                }

                // Regardless of success or failure, cache the plug-in load
                // in our plug-in info so we don't try to load it again and 
                // again.
                SetPluginInfo (plugin_file_spec, plugin_info);

                return FileSpec::eEnumerateDirectoryResultNext;
            }
        }
    }
    
    if (file_type == FileSpec::eFileTypeUnknown     ||
        file_type == FileSpec::eFileTypeDirectory   ||
        file_type == FileSpec::eFileTypeSymbolicLink )
    {
        // Try and recurse into anything that a directory or symbolic link. 
        // We must also do this for unknown as sometimes the directory enumeration
        // might be enumerating a file system that doesn't have correct file type
        // information.
        return FileSpec::eEnumerateDirectoryResultEnter;
    }

    return FileSpec::eEnumerateDirectoryResultNext;
}

void
PluginManager::Initialize ()
{
#if 1
    FileSpec dir_spec;
    const bool find_directories = true;
    const bool find_files = true;
    const bool find_other = true;
    char dir_path[PATH_MAX];
    if (HostInfo::GetLLDBPath(ePathTypeLLDBSystemPlugins, dir_spec))
    {
        if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path)))
        {
            FileSpec::EnumerateDirectory(dir_path,
                                         find_directories,
                                         find_files,
                                         find_other,
                                         LoadPluginCallback,
                                         nullptr);
        }
    }

    if (HostInfo::GetLLDBPath(ePathTypeLLDBUserPlugins, dir_spec))
    {
        if (dir_spec.Exists() && dir_spec.GetPath(dir_path, sizeof(dir_path)))
        {
            FileSpec::EnumerateDirectory(dir_path,
                                         find_directories,
                                         find_files,
                                         find_other,
                                         LoadPluginCallback,
                                         nullptr);
        }
    }
#endif
}

void
PluginManager::Terminate ()
{
    Mutex::Locker locker (GetPluginMapMutex ());
    PluginTerminateMap &plugin_map = GetPluginMap ();
    
    PluginTerminateMap::const_iterator pos, end = plugin_map.end();
    for (pos = plugin_map.begin(); pos != end; ++pos)
    {
        // Call the plug-in "void LLDBPluginTerminate (void)" function if there
        // is one (if the symbol was not nullptr).
        if (pos->second.library.isValid())
        {
            if (pos->second.plugin_term_callback)
                pos->second.plugin_term_callback();
        }
    }
    plugin_map.clear();
}

#pragma mark ABI

struct ABIInstance
{
    ABIInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    ABICreateInstance create_callback;
};

typedef std::vector<ABIInstance> ABIInstances;

static Mutex &
GetABIInstancesMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static ABIInstances &
GetABIInstances ()
{
    static ABIInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              ABICreateInstance create_callback)
{
    if (create_callback)
    {
        ABIInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetABIInstancesMutex ());
        GetABIInstances ().push_back (instance);
        return true;
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (ABICreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetABIInstancesMutex ());
        ABIInstances &instances = GetABIInstances ();

        ABIInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

ABICreateInstance
PluginManager::GetABICreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetABIInstancesMutex ());
    ABIInstances &instances = GetABIInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

ABICreateInstance
PluginManager::GetABICreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetABIInstancesMutex ());
        ABIInstances &instances = GetABIInstances ();

        ABIInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark Disassembler

struct DisassemblerInstance
{
    DisassemblerInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    DisassemblerCreateInstance create_callback;
};

typedef std::vector<DisassemblerInstance> DisassemblerInstances;

static Mutex &
GetDisassemblerMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static DisassemblerInstances &
GetDisassemblerInstances ()
{
    static DisassemblerInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              DisassemblerCreateInstance create_callback)
{
    if (create_callback)
    {
        DisassemblerInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetDisassemblerMutex ());
        GetDisassemblerInstances ().push_back (instance);
        return true;
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (DisassemblerCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetDisassemblerMutex ());
        DisassemblerInstances &instances = GetDisassemblerInstances ();
        
        DisassemblerInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

DisassemblerCreateInstance
PluginManager::GetDisassemblerCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetDisassemblerMutex ());
    DisassemblerInstances &instances = GetDisassemblerInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

DisassemblerCreateInstance
PluginManager::GetDisassemblerCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetDisassemblerMutex ());
        DisassemblerInstances &instances = GetDisassemblerInstances ();
        
        DisassemblerInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark DynamicLoader

struct DynamicLoaderInstance
{
    DynamicLoaderInstance() :
        name(),
        description(),
        create_callback(nullptr),
        debugger_init_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    DynamicLoaderCreateInstance create_callback;
    DebuggerInitializeCallback debugger_init_callback;
};

typedef std::vector<DynamicLoaderInstance> DynamicLoaderInstances;

static Mutex &
GetDynamicLoaderMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static DynamicLoaderInstances &
GetDynamicLoaderInstances ()
{
    static DynamicLoaderInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              DynamicLoaderCreateInstance create_callback,
                              DebuggerInitializeCallback debugger_init_callback)
{
    if (create_callback)
    {
        DynamicLoaderInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.debugger_init_callback = debugger_init_callback;
        Mutex::Locker locker (GetDynamicLoaderMutex ());
        GetDynamicLoaderInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (DynamicLoaderCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetDynamicLoaderMutex ());
        DynamicLoaderInstances &instances = GetDynamicLoaderInstances ();
        
        DynamicLoaderInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

DynamicLoaderCreateInstance
PluginManager::GetDynamicLoaderCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetDynamicLoaderMutex ());
    DynamicLoaderInstances &instances = GetDynamicLoaderInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

DynamicLoaderCreateInstance
PluginManager::GetDynamicLoaderCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetDynamicLoaderMutex ());
        DynamicLoaderInstances &instances = GetDynamicLoaderInstances ();
        
        DynamicLoaderInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark JITLoader

struct JITLoaderInstance
{
    JITLoaderInstance() :
        name(),
        description(),
        create_callback(nullptr),
        debugger_init_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    JITLoaderCreateInstance create_callback;
    DebuggerInitializeCallback debugger_init_callback;
};

typedef std::vector<JITLoaderInstance> JITLoaderInstances;

static Mutex &
GetJITLoaderMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static JITLoaderInstances &
GetJITLoaderInstances ()
{
    static JITLoaderInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              JITLoaderCreateInstance create_callback,
                              DebuggerInitializeCallback debugger_init_callback)
{
    if (create_callback)
    {
        JITLoaderInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.debugger_init_callback = debugger_init_callback;
        Mutex::Locker locker (GetJITLoaderMutex ());
        GetJITLoaderInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (JITLoaderCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetJITLoaderMutex ());
        JITLoaderInstances &instances = GetJITLoaderInstances ();
        
        JITLoaderInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

JITLoaderCreateInstance
PluginManager::GetJITLoaderCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetJITLoaderMutex ());
    JITLoaderInstances &instances = GetJITLoaderInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

JITLoaderCreateInstance
PluginManager::GetJITLoaderCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetJITLoaderMutex ());
        JITLoaderInstances &instances = GetJITLoaderInstances ();
        
        JITLoaderInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark EmulateInstruction

struct EmulateInstructionInstance
{
    EmulateInstructionInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    EmulateInstructionCreateInstance create_callback;
};

typedef std::vector<EmulateInstructionInstance> EmulateInstructionInstances;

static Mutex &
GetEmulateInstructionMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static EmulateInstructionInstances &
GetEmulateInstructionInstances ()
{
    static EmulateInstructionInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              EmulateInstructionCreateInstance create_callback)
{
    if (create_callback)
    {
        EmulateInstructionInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetEmulateInstructionMutex ());
        GetEmulateInstructionInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (EmulateInstructionCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetEmulateInstructionMutex ());
        EmulateInstructionInstances &instances = GetEmulateInstructionInstances ();
        
        EmulateInstructionInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

EmulateInstructionCreateInstance
PluginManager::GetEmulateInstructionCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetEmulateInstructionMutex ());
    EmulateInstructionInstances &instances = GetEmulateInstructionInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

EmulateInstructionCreateInstance
PluginManager::GetEmulateInstructionCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetEmulateInstructionMutex ());
        EmulateInstructionInstances &instances = GetEmulateInstructionInstances ();
        
        EmulateInstructionInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark OperatingSystem

struct OperatingSystemInstance
{
    OperatingSystemInstance () :
        name (),
        description (),
        create_callback (nullptr),
        debugger_init_callback (nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    OperatingSystemCreateInstance create_callback;
    DebuggerInitializeCallback debugger_init_callback;
};

typedef std::vector<OperatingSystemInstance> OperatingSystemInstances;

static Mutex &
GetOperatingSystemMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static OperatingSystemInstances &
GetOperatingSystemInstances ()
{
    static OperatingSystemInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name, const char *description,
                              OperatingSystemCreateInstance create_callback,
                              DebuggerInitializeCallback debugger_init_callback)
{
    if (create_callback)
    {
        OperatingSystemInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.debugger_init_callback = debugger_init_callback;
        Mutex::Locker locker (GetOperatingSystemMutex ());
        GetOperatingSystemInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (OperatingSystemCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetOperatingSystemMutex ());
        OperatingSystemInstances &instances = GetOperatingSystemInstances ();
        
        OperatingSystemInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

OperatingSystemCreateInstance
PluginManager::GetOperatingSystemCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetOperatingSystemMutex ());
    OperatingSystemInstances &instances = GetOperatingSystemInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

OperatingSystemCreateInstance
PluginManager::GetOperatingSystemCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetOperatingSystemMutex ());
        OperatingSystemInstances &instances = GetOperatingSystemInstances ();
        
        OperatingSystemInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark Language

struct LanguageInstance
{
    LanguageInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    LanguageCreateInstance create_callback;
};

typedef std::vector<LanguageInstance> LanguageInstances;

static Mutex &
GetLanguageMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static LanguageInstances &
GetLanguageInstances ()
{
    static LanguageInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              LanguageCreateInstance create_callback)
{
    if (create_callback)
    {
        LanguageInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetLanguageMutex ());
        GetLanguageInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (LanguageCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetLanguageMutex ());
        LanguageInstances &instances = GetLanguageInstances ();
        
        LanguageInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

LanguageCreateInstance
PluginManager::GetLanguageCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetLanguageMutex ());
    LanguageInstances &instances = GetLanguageInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

LanguageCreateInstance
PluginManager::GetLanguageCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetLanguageMutex ());
        LanguageInstances &instances = GetLanguageInstances ();
        
        LanguageInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark LanguageRuntime

struct LanguageRuntimeInstance
{
    LanguageRuntimeInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    LanguageRuntimeCreateInstance create_callback;
    LanguageRuntimeGetCommandObject command_callback;
};

typedef std::vector<LanguageRuntimeInstance> LanguageRuntimeInstances;

static Mutex &
GetLanguageRuntimeMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static LanguageRuntimeInstances &
GetLanguageRuntimeInstances ()
{
    static LanguageRuntimeInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              LanguageRuntimeCreateInstance create_callback,
                              LanguageRuntimeGetCommandObject command_callback)
{
    if (create_callback)
    {
        LanguageRuntimeInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.command_callback = command_callback;
        Mutex::Locker locker (GetLanguageRuntimeMutex ());
        GetLanguageRuntimeInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (LanguageRuntimeCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetLanguageRuntimeMutex ());
        LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances ();
        
        LanguageRuntimeInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

LanguageRuntimeCreateInstance
PluginManager::GetLanguageRuntimeCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetLanguageRuntimeMutex ());
    LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

LanguageRuntimeGetCommandObject
PluginManager::GetLanguageRuntimeGetCommandObjectAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetLanguageRuntimeMutex ());
    LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances ();
    if (idx < instances.size())
        return instances[idx].command_callback;
    return nullptr;
}

LanguageRuntimeCreateInstance
PluginManager::GetLanguageRuntimeCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetLanguageRuntimeMutex ());
        LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances ();
        
        LanguageRuntimeInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark SystemRuntime

struct SystemRuntimeInstance
{
    SystemRuntimeInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    SystemRuntimeCreateInstance create_callback;
};

typedef std::vector<SystemRuntimeInstance> SystemRuntimeInstances;

static Mutex &
GetSystemRuntimeMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static SystemRuntimeInstances &
GetSystemRuntimeInstances ()
{
    static SystemRuntimeInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              SystemRuntimeCreateInstance create_callback)
{
    if (create_callback)
    {
        SystemRuntimeInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetSystemRuntimeMutex ());
        GetSystemRuntimeInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (SystemRuntimeCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetSystemRuntimeMutex ());
        SystemRuntimeInstances &instances = GetSystemRuntimeInstances ();
        
        SystemRuntimeInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

SystemRuntimeCreateInstance
PluginManager::GetSystemRuntimeCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetSystemRuntimeMutex ());
    SystemRuntimeInstances &instances = GetSystemRuntimeInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

SystemRuntimeCreateInstance
PluginManager::GetSystemRuntimeCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetSystemRuntimeMutex ());
        SystemRuntimeInstances &instances = GetSystemRuntimeInstances ();
        
        SystemRuntimeInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark ObjectFile

struct ObjectFileInstance
{
    ObjectFileInstance() :
        name(),
        description(),
        create_callback(nullptr),
        create_memory_callback(nullptr),
        get_module_specifications(nullptr),
        save_core(nullptr)
    {
    }

    ConstString name;
    std::string description;
    ObjectFileCreateInstance create_callback;
    ObjectFileCreateMemoryInstance create_memory_callback;
    ObjectFileGetModuleSpecifications get_module_specifications;
    ObjectFileSaveCore save_core;
};

typedef std::vector<ObjectFileInstance> ObjectFileInstances;

static Mutex &
GetObjectFileMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static ObjectFileInstances &
GetObjectFileInstances ()
{
    static ObjectFileInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin (const ConstString &name,
                               const char *description,
                               ObjectFileCreateInstance create_callback,
                               ObjectFileCreateMemoryInstance create_memory_callback,
                               ObjectFileGetModuleSpecifications get_module_specifications,
                               ObjectFileSaveCore save_core)
{
    if (create_callback)
    {
        ObjectFileInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.create_memory_callback = create_memory_callback;
        instance.save_core = save_core;
        instance.get_module_specifications = get_module_specifications;
        Mutex::Locker locker (GetObjectFileMutex ());
        GetObjectFileInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (ObjectFileCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetObjectFileMutex ());
        ObjectFileInstances &instances = GetObjectFileInstances ();
        
        ObjectFileInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

ObjectFileCreateInstance
PluginManager::GetObjectFileCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetObjectFileMutex ());
    ObjectFileInstances &instances = GetObjectFileInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

ObjectFileCreateMemoryInstance
PluginManager::GetObjectFileCreateMemoryCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetObjectFileMutex ());
    ObjectFileInstances &instances = GetObjectFileInstances ();
    if (idx < instances.size())
        return instances[idx].create_memory_callback;
    return nullptr;
}

ObjectFileGetModuleSpecifications
PluginManager::GetObjectFileGetModuleSpecificationsCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetObjectFileMutex ());
    ObjectFileInstances &instances = GetObjectFileInstances ();
    if (idx < instances.size())
        return instances[idx].get_module_specifications;
    return nullptr;
}

ObjectFileCreateInstance
PluginManager::GetObjectFileCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetObjectFileMutex ());
        ObjectFileInstances &instances = GetObjectFileInstances ();
        
        ObjectFileInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

ObjectFileCreateMemoryInstance
PluginManager::GetObjectFileCreateMemoryCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetObjectFileMutex ());
        ObjectFileInstances &instances = GetObjectFileInstances ();
        
        ObjectFileInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_memory_callback;
        }
    }
    return nullptr;
}

Error
PluginManager::SaveCore (const lldb::ProcessSP &process_sp, const FileSpec &outfile)
{
    Error error;
    Mutex::Locker locker (GetObjectFileMutex ());
    ObjectFileInstances &instances = GetObjectFileInstances ();
    
    ObjectFileInstances::iterator pos, end = instances.end();
    for (pos = instances.begin(); pos != end; ++ pos)
    {
        if (pos->save_core && pos->save_core (process_sp, outfile, error))
            return error;
    }
    error.SetErrorString("no ObjectFile plugins were able to save a core for this process");
    return error;
}

#pragma mark ObjectContainer

struct ObjectContainerInstance
{
    ObjectContainerInstance() :
        name(),
        description(),
        create_callback(nullptr),
        get_module_specifications(nullptr)
    {
    }

    ConstString name;
    std::string description;
    ObjectContainerCreateInstance create_callback;
    ObjectFileGetModuleSpecifications get_module_specifications;
};

typedef std::vector<ObjectContainerInstance> ObjectContainerInstances;

static Mutex &
GetObjectContainerMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static ObjectContainerInstances &
GetObjectContainerInstances ()
{
    static ObjectContainerInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin (const ConstString &name,
                               const char *description,
                               ObjectContainerCreateInstance create_callback,
                               ObjectFileGetModuleSpecifications get_module_specifications)
{
    if (create_callback)
    {
        ObjectContainerInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.get_module_specifications = get_module_specifications;
        Mutex::Locker locker (GetObjectContainerMutex ());
        GetObjectContainerInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (ObjectContainerCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetObjectContainerMutex ());
        ObjectContainerInstances &instances = GetObjectContainerInstances ();
        
        ObjectContainerInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

ObjectContainerCreateInstance
PluginManager::GetObjectContainerCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetObjectContainerMutex ());
    ObjectContainerInstances &instances = GetObjectContainerInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

ObjectContainerCreateInstance
PluginManager::GetObjectContainerCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetObjectContainerMutex ());
        ObjectContainerInstances &instances = GetObjectContainerInstances ();
        
        ObjectContainerInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

ObjectFileGetModuleSpecifications
PluginManager::GetObjectContainerGetModuleSpecificationsCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetObjectContainerMutex ());
    ObjectContainerInstances &instances = GetObjectContainerInstances ();
    if (idx < instances.size())
        return instances[idx].get_module_specifications;
    return nullptr;
}

#pragma mark LogChannel

struct LogInstance
{
    LogInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    LogChannelCreateInstance create_callback;
};

typedef std::vector<LogInstance> LogInstances;

static Mutex &
GetLogMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static LogInstances &
GetLogInstances ()
{
    static LogInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              LogChannelCreateInstance create_callback)
{
    if (create_callback)
    {
        LogInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetLogMutex ());
        GetLogInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (LogChannelCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetLogMutex ());
        LogInstances &instances = GetLogInstances ();
        
        LogInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

const char *
PluginManager::GetLogChannelCreateNameAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetLogMutex ());
    LogInstances &instances = GetLogInstances ();
    if (idx < instances.size())
        return instances[idx].name.GetCString();
    return nullptr;
}

LogChannelCreateInstance
PluginManager::GetLogChannelCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetLogMutex ());
    LogInstances &instances = GetLogInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

LogChannelCreateInstance
PluginManager::GetLogChannelCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetLogMutex ());
        LogInstances &instances = GetLogInstances ();
        
        LogInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark Platform

struct PlatformInstance
{
    PlatformInstance() :
        name(),
        description(),
        create_callback(nullptr),
        debugger_init_callback(nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    PlatformCreateInstance create_callback;
    DebuggerInitializeCallback debugger_init_callback;
};

typedef std::vector<PlatformInstance> PlatformInstances;

static Mutex &
GetPlatformInstancesMutex ()
{
    static Mutex g_platform_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_platform_instances_mutex;
}

static PlatformInstances &
GetPlatformInstances ()
{
    static PlatformInstances g_platform_instances;
    return g_platform_instances;
}

bool
PluginManager::RegisterPlugin (const ConstString &name,
                               const char *description,
                               PlatformCreateInstance create_callback,
                               DebuggerInitializeCallback debugger_init_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetPlatformInstancesMutex ());
        
        PlatformInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.debugger_init_callback = debugger_init_callback;
        GetPlatformInstances ().push_back (instance);
        return true;
    }
    return false;
}

const char *
PluginManager::GetPlatformPluginNameAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetPlatformInstancesMutex ());
    PlatformInstances &instances = GetPlatformInstances ();
    if (idx < instances.size())
        return instances[idx].name.GetCString();
    return nullptr;
}

const char *
PluginManager::GetPlatformPluginDescriptionAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetPlatformInstancesMutex ());
    PlatformInstances &instances = GetPlatformInstances ();
    if (idx < instances.size())
        return instances[idx].description.c_str();
    return nullptr;
}

bool
PluginManager::UnregisterPlugin (PlatformCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetPlatformInstancesMutex ());
        PlatformInstances &instances = GetPlatformInstances ();

        PlatformInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

PlatformCreateInstance
PluginManager::GetPlatformCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetPlatformInstancesMutex ());
    PlatformInstances &instances = GetPlatformInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

PlatformCreateInstance
PluginManager::GetPlatformCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetPlatformInstancesMutex ());
        PlatformInstances &instances = GetPlatformInstances ();

        PlatformInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

size_t
PluginManager::AutoCompletePlatformName (const char *name, StringList &matches)
{
    if (name)
    {
        Mutex::Locker locker (GetPlatformInstancesMutex ());
        PlatformInstances &instances = GetPlatformInstances ();
        llvm::StringRef name_sref(name);

        PlatformInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            llvm::StringRef plugin_name (pos->name.GetCString());
            if (plugin_name.startswith(name_sref))
                matches.AppendString (plugin_name.data());
        }
    }
    return matches.GetSize();
}

#pragma mark Process

struct ProcessInstance
{
    ProcessInstance() :
        name(),
        description(),
        create_callback(nullptr),
        debugger_init_callback(nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    ProcessCreateInstance create_callback;
    DebuggerInitializeCallback debugger_init_callback;
};

typedef std::vector<ProcessInstance> ProcessInstances;

static Mutex &
GetProcessMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static ProcessInstances &
GetProcessInstances ()
{
    static ProcessInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin (const ConstString &name,
                               const char *description,
                               ProcessCreateInstance create_callback,
                               DebuggerInitializeCallback debugger_init_callback)
{
    if (create_callback)
    {
        ProcessInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.debugger_init_callback = debugger_init_callback;
        Mutex::Locker locker (GetProcessMutex ());
        GetProcessInstances ().push_back (instance);
    }
    return false;
}

const char *
PluginManager::GetProcessPluginNameAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetProcessMutex ());
    ProcessInstances &instances = GetProcessInstances ();
    if (idx < instances.size())
        return instances[idx].name.GetCString();
    return nullptr;
}

const char *
PluginManager::GetProcessPluginDescriptionAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetProcessMutex ());
    ProcessInstances &instances = GetProcessInstances ();
    if (idx < instances.size())
        return instances[idx].description.c_str();
    return nullptr;
}

bool
PluginManager::UnregisterPlugin (ProcessCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetProcessMutex ());
        ProcessInstances &instances = GetProcessInstances ();
        
        ProcessInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

ProcessCreateInstance
PluginManager::GetProcessCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetProcessMutex ());
    ProcessInstances &instances = GetProcessInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

ProcessCreateInstance
PluginManager::GetProcessCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetProcessMutex ());
        ProcessInstances &instances = GetProcessInstances ();
        
        ProcessInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark ScriptInterpreter

struct ScriptInterpreterInstance
{
    ScriptInterpreterInstance()
        : name()
        , language(lldb::eScriptLanguageNone)
        , description()
        , create_callback(nullptr)
    {
    }

    ConstString name;
    lldb::ScriptLanguage language;
    std::string description;
    ScriptInterpreterCreateInstance create_callback;
};

typedef std::vector<ScriptInterpreterInstance> ScriptInterpreterInstances;

static Mutex &
GetScriptInterpreterMutex()
{
    static Mutex g_instances_mutex(Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static ScriptInterpreterInstances &
GetScriptInterpreterInstances()
{
    static ScriptInterpreterInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name, const char *description, lldb::ScriptLanguage script_language,
                              ScriptInterpreterCreateInstance create_callback)
{
    if (!create_callback)
        return false;
    ScriptInterpreterInstance instance;
    assert((bool)name);
    instance.name = name;
    if (description && description[0])
        instance.description = description;
    instance.create_callback = create_callback;
    instance.language = script_language;
    Mutex::Locker locker(GetScriptInterpreterMutex());
    GetScriptInterpreterInstances().push_back(instance);
    return false;
}

bool
PluginManager::UnregisterPlugin(ScriptInterpreterCreateInstance create_callback)
{
    if (!create_callback)
        return false;
    Mutex::Locker locker(GetScriptInterpreterMutex());
    ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();

    ScriptInterpreterInstances::iterator pos, end = instances.end();
    for (pos = instances.begin(); pos != end; ++pos)
    {
        if (pos->create_callback != create_callback)
            continue;

        instances.erase(pos);
        return true;
    }
    return false;
}

ScriptInterpreterCreateInstance
PluginManager::GetScriptInterpreterCreateCallbackAtIndex(uint32_t idx)
{
    Mutex::Locker locker(GetScriptInterpreterMutex());
    ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

lldb::ScriptInterpreterSP
PluginManager::GetScriptInterpreterForLanguage(lldb::ScriptLanguage script_lang, CommandInterpreter &interpreter)
{
    Mutex::Locker locker(GetScriptInterpreterMutex());
    ScriptInterpreterInstances &instances = GetScriptInterpreterInstances();

    ScriptInterpreterInstances::iterator pos, end = instances.end();
    ScriptInterpreterCreateInstance none_instance = nullptr;
    for (pos = instances.begin(); pos != end; ++pos)
    {
        if (pos->language == lldb::eScriptLanguageNone)
            none_instance = pos->create_callback;

        if (script_lang == pos->language)
            return pos->create_callback(interpreter);
    }

    // If we didn't find one, return the ScriptInterpreter for the null language.
    assert(none_instance != nullptr);
    return none_instance(interpreter);
}

#pragma mark SymbolFile

struct SymbolFileInstance
{
    SymbolFileInstance() :
        name(),
        description(),
        create_callback(nullptr),
        debugger_init_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    SymbolFileCreateInstance create_callback;
    DebuggerInitializeCallback debugger_init_callback;
};

typedef std::vector<SymbolFileInstance> SymbolFileInstances;

static Mutex &
GetSymbolFileMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static SymbolFileInstances &
GetSymbolFileInstances ()
{
    static SymbolFileInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              SymbolFileCreateInstance create_callback,
                              DebuggerInitializeCallback debugger_init_callback)
{
    if (create_callback)
    {
        SymbolFileInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.debugger_init_callback = debugger_init_callback;
        Mutex::Locker locker (GetSymbolFileMutex ());
        GetSymbolFileInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (SymbolFileCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetSymbolFileMutex ());
        SymbolFileInstances &instances = GetSymbolFileInstances ();
        
        SymbolFileInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

SymbolFileCreateInstance
PluginManager::GetSymbolFileCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetSymbolFileMutex ());
    SymbolFileInstances &instances = GetSymbolFileInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

SymbolFileCreateInstance
PluginManager::GetSymbolFileCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetSymbolFileMutex ());
        SymbolFileInstances &instances = GetSymbolFileInstances ();
        
        SymbolFileInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark SymbolVendor

struct SymbolVendorInstance
{
    SymbolVendorInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    SymbolVendorCreateInstance create_callback;
};

typedef std::vector<SymbolVendorInstance> SymbolVendorInstances;

static Mutex &
GetSymbolVendorMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static SymbolVendorInstances &
GetSymbolVendorInstances ()
{
    static SymbolVendorInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              SymbolVendorCreateInstance create_callback)
{
    if (create_callback)
    {
        SymbolVendorInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetSymbolVendorMutex ());
        GetSymbolVendorInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (SymbolVendorCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetSymbolVendorMutex ());
        SymbolVendorInstances &instances = GetSymbolVendorInstances ();
        
        SymbolVendorInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

SymbolVendorCreateInstance
PluginManager::GetSymbolVendorCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetSymbolVendorMutex ());
    SymbolVendorInstances &instances = GetSymbolVendorInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

SymbolVendorCreateInstance
PluginManager::GetSymbolVendorCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetSymbolVendorMutex ());
        SymbolVendorInstances &instances = GetSymbolVendorInstances ();
        
        SymbolVendorInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark UnwindAssembly

struct UnwindAssemblyInstance
{
    UnwindAssemblyInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    UnwindAssemblyCreateInstance create_callback;
};

typedef std::vector<UnwindAssemblyInstance> UnwindAssemblyInstances;

static Mutex &
GetUnwindAssemblyMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static UnwindAssemblyInstances &
GetUnwindAssemblyInstances ()
{
    static UnwindAssemblyInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              UnwindAssemblyCreateInstance create_callback)
{
    if (create_callback)
    {
        UnwindAssemblyInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetUnwindAssemblyMutex ());
        GetUnwindAssemblyInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (UnwindAssemblyCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetUnwindAssemblyMutex ());
        UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances ();
        
        UnwindAssemblyInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

UnwindAssemblyCreateInstance
PluginManager::GetUnwindAssemblyCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetUnwindAssemblyMutex ());
    UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

UnwindAssemblyCreateInstance
PluginManager::GetUnwindAssemblyCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetUnwindAssemblyMutex ());
        UnwindAssemblyInstances &instances = GetUnwindAssemblyInstances ();
        
        UnwindAssemblyInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark MemoryHistory

struct MemoryHistoryInstance
{
    MemoryHistoryInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    MemoryHistoryCreateInstance create_callback;
};

typedef std::vector<MemoryHistoryInstance> MemoryHistoryInstances;

static Mutex &
GetMemoryHistoryMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static MemoryHistoryInstances &
GetMemoryHistoryInstances ()
{
    static MemoryHistoryInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              MemoryHistoryCreateInstance create_callback)
{
    if (create_callback)
    {
        MemoryHistoryInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        Mutex::Locker locker (GetMemoryHistoryMutex ());
        GetMemoryHistoryInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (MemoryHistoryCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetMemoryHistoryMutex ());
        MemoryHistoryInstances &instances = GetMemoryHistoryInstances ();
        
        MemoryHistoryInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

MemoryHistoryCreateInstance
PluginManager::GetMemoryHistoryCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetMemoryHistoryMutex ());
    MemoryHistoryInstances &instances = GetMemoryHistoryInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

MemoryHistoryCreateInstance
PluginManager::GetMemoryHistoryCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetMemoryHistoryMutex ());
        MemoryHistoryInstances &instances = GetMemoryHistoryInstances ();
        
        MemoryHistoryInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark InstrumentationRuntime

struct InstrumentationRuntimeInstance
{
    InstrumentationRuntimeInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    InstrumentationRuntimeCreateInstance create_callback;
    InstrumentationRuntimeGetType get_type_callback;
};

typedef std::vector<InstrumentationRuntimeInstance> InstrumentationRuntimeInstances;

static Mutex &
GetInstrumentationRuntimeMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static InstrumentationRuntimeInstances &
GetInstrumentationRuntimeInstances ()
{
    static InstrumentationRuntimeInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin(const ConstString &name,
                              const char *description,
                              InstrumentationRuntimeCreateInstance create_callback,
                              InstrumentationRuntimeGetType get_type_callback)
{
    if (create_callback)
    {
        InstrumentationRuntimeInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.get_type_callback = get_type_callback;
        Mutex::Locker locker (GetInstrumentationRuntimeMutex ());
        GetInstrumentationRuntimeInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (InstrumentationRuntimeCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetInstrumentationRuntimeMutex ());
        InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances ();
        
        InstrumentationRuntimeInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

InstrumentationRuntimeGetType
PluginManager::GetInstrumentationRuntimeGetTypeCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetInstrumentationRuntimeMutex ());
    InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances ();
    if (idx < instances.size())
        return instances[idx].get_type_callback;
    return nullptr;
}

InstrumentationRuntimeCreateInstance
PluginManager::GetInstrumentationRuntimeCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetInstrumentationRuntimeMutex ());
    InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

InstrumentationRuntimeCreateInstance
PluginManager::GetInstrumentationRuntimeCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetInstrumentationRuntimeMutex ());
        InstrumentationRuntimeInstances &instances = GetInstrumentationRuntimeInstances ();
        
        InstrumentationRuntimeInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

#pragma mark TypeSystem

struct TypeSystemInstance
{
    TypeSystemInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }

    ConstString name;
    std::string description;
    TypeSystemCreateInstance create_callback;
    TypeSystemEnumerateSupportedLanguages enumerate_callback;
};

typedef std::vector<TypeSystemInstance> TypeSystemInstances;

static Mutex &
GetTypeSystemMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static TypeSystemInstances &
GetTypeSystemInstances ()
{
    static TypeSystemInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin (const ConstString &name,
                               const char *description,
                               TypeSystemCreateInstance create_callback,
                               TypeSystemEnumerateSupportedLanguages enumerate_supported_languages_callback)
{
    if (create_callback)
    {
        TypeSystemInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.enumerate_callback = enumerate_supported_languages_callback;
        Mutex::Locker locker (GetTypeSystemMutex ());
        GetTypeSystemInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (TypeSystemCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetTypeSystemMutex ());
        TypeSystemInstances &instances = GetTypeSystemInstances ();

        TypeSystemInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

TypeSystemCreateInstance
PluginManager::GetTypeSystemCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetTypeSystemMutex ());
    TypeSystemInstances &instances = GetTypeSystemInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

TypeSystemCreateInstance
PluginManager::GetTypeSystemCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetTypeSystemMutex ());
        TypeSystemInstances &instances = GetTypeSystemInstances ();

        TypeSystemInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

TypeSystemEnumerateSupportedLanguages
PluginManager::GetTypeSystemEnumerateSupportedLanguagesCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetTypeSystemMutex ());
    TypeSystemInstances &instances = GetTypeSystemInstances ();
    if (idx < instances.size())
        return instances[idx].enumerate_callback;
    return nullptr;
}

TypeSystemEnumerateSupportedLanguages
PluginManager::GetTypeSystemEnumerateSupportedLanguagesCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetTypeSystemMutex ());
        TypeSystemInstances &instances = GetTypeSystemInstances ();
        
        TypeSystemInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->enumerate_callback;
        }
    }
    return nullptr;
}

#pragma mark REPL

struct REPLInstance
{
    REPLInstance() :
        name(),
        description(),
        create_callback(nullptr)
    {
    }
    
    ConstString name;
    std::string description;
    REPLCreateInstance create_callback;
    REPLEnumerateSupportedLanguages enumerate_languages_callback;
};

typedef std::vector<REPLInstance> REPLInstances;

static Mutex &
GetREPLMutex ()
{
    static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive);
    return g_instances_mutex;
}

static REPLInstances &
GetREPLInstances ()
{
    static REPLInstances g_instances;
    return g_instances;
}

bool
PluginManager::RegisterPlugin (const ConstString &name,
                               const char *description,
                               REPLCreateInstance create_callback,
                               REPLEnumerateSupportedLanguages enumerate_languages_callback)
{
    if (create_callback)
    {
        REPLInstance instance;
        assert ((bool)name);
        instance.name = name;
        if (description && description[0])
            instance.description = description;
        instance.create_callback = create_callback;
        instance.enumerate_languages_callback = enumerate_languages_callback;
        Mutex::Locker locker (GetREPLMutex ());
        GetREPLInstances ().push_back (instance);
    }
    return false;
}

bool
PluginManager::UnregisterPlugin (REPLCreateInstance create_callback)
{
    if (create_callback)
    {
        Mutex::Locker locker (GetREPLMutex ());
        REPLInstances &instances = GetREPLInstances ();
        
        REPLInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->create_callback == create_callback)
            {
                instances.erase(pos);
                return true;
            }
        }
    }
    return false;
}

REPLCreateInstance
PluginManager::GetREPLCreateCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetREPLMutex ());
    REPLInstances &instances = GetREPLInstances ();
    if (idx < instances.size())
        return instances[idx].create_callback;
    return nullptr;
}

REPLCreateInstance
PluginManager::GetREPLCreateCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetREPLMutex ());
        REPLInstances &instances = GetREPLInstances ();
        
        REPLInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->create_callback;
        }
    }
    return nullptr;
}

REPLEnumerateSupportedLanguages
PluginManager::GetREPLEnumerateSupportedLanguagesCallbackAtIndex (uint32_t idx)
{
    Mutex::Locker locker (GetREPLMutex ());
    REPLInstances &instances = GetREPLInstances ();
    if (idx < instances.size())
        return instances[idx].enumerate_languages_callback;
    return nullptr;
}

REPLEnumerateSupportedLanguages
PluginManager::GetREPLSystemEnumerateSupportedLanguagesCallbackForPluginName (const ConstString &name)
{
    if (name)
    {
        Mutex::Locker locker (GetREPLMutex ());
        REPLInstances &instances = GetREPLInstances ();
        
        REPLInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (name == pos->name)
                return pos->enumerate_languages_callback;
        }
    }
    return nullptr;
}

#pragma mark PluginManager

void
PluginManager::DebuggerInitialize (Debugger &debugger)
{
    // Initialize the DynamicLoader plugins
    {
        Mutex::Locker locker (GetDynamicLoaderMutex ());
        DynamicLoaderInstances &instances = GetDynamicLoaderInstances ();
    
        DynamicLoaderInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->debugger_init_callback)
                pos->debugger_init_callback (debugger);
        }
    }

    // Initialize the JITLoader plugins
    {
        Mutex::Locker locker (GetJITLoaderMutex ());
        JITLoaderInstances &instances = GetJITLoaderInstances ();
    
        JITLoaderInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->debugger_init_callback)
                pos->debugger_init_callback (debugger);
        }
    }

    // Initialize the Platform plugins
    {
        Mutex::Locker locker (GetPlatformInstancesMutex ());
        PlatformInstances &instances = GetPlatformInstances ();
    
        PlatformInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->debugger_init_callback)
                pos->debugger_init_callback (debugger);
        }
    }

    // Initialize the Process plugins
    {
        Mutex::Locker locker (GetProcessMutex());
        ProcessInstances &instances = GetProcessInstances();
        
        ProcessInstances::iterator pos, end = instances.end();
        for (pos = instances.begin(); pos != end; ++ pos)
        {
            if (pos->debugger_init_callback)
                pos->debugger_init_callback (debugger);
        }
    }

    // Initialize the SymbolFile plugins
    {
        Mutex::Locker locker (GetSymbolFileMutex());
        for (auto& sym_file: GetSymbolFileInstances())
        {
            if (sym_file.debugger_init_callback)
                sym_file.debugger_init_callback (debugger);
        }
    }

    // Initialize the OperatingSystem plugins
    {
        Mutex::Locker locker(GetOperatingSystemMutex());
        for (auto &os : GetOperatingSystemInstances())
        {
            if (os.debugger_init_callback)
                os.debugger_init_callback(debugger);
        }
    }
}

// This is the preferred new way to register plugin specific settings.  e.g.
// This will put a plugin's settings under e.g. "plugin.<plugin_type_name>.<plugin_type_desc>.SETTINGNAME".
static lldb::OptionValuePropertiesSP
GetDebuggerPropertyForPlugins (Debugger &debugger,
                                       const ConstString &plugin_type_name,
                                       const ConstString &plugin_type_desc,
                                       bool can_create)
{
    lldb::OptionValuePropertiesSP parent_properties_sp (debugger.GetValueProperties());
    if (parent_properties_sp)
    {
        static ConstString g_property_name("plugin");
        
        OptionValuePropertiesSP plugin_properties_sp = parent_properties_sp->GetSubProperty(nullptr, g_property_name);
        if (!plugin_properties_sp && can_create)
        {
            plugin_properties_sp.reset (new OptionValueProperties (g_property_name));
            parent_properties_sp->AppendProperty (g_property_name,
                                                  ConstString("Settings specify to plugins."),
                                                  true,
                                                  plugin_properties_sp);
        }
        
        if (plugin_properties_sp)
        {
            lldb::OptionValuePropertiesSP plugin_type_properties_sp = plugin_properties_sp->GetSubProperty(nullptr, plugin_type_name);
            if (!plugin_type_properties_sp && can_create)
            {
                plugin_type_properties_sp.reset (new OptionValueProperties (plugin_type_name));
                plugin_properties_sp->AppendProperty (plugin_type_name,
                                                      plugin_type_desc,
                                                      true,
                                                      plugin_type_properties_sp);
            }
            return plugin_type_properties_sp;
        }
    }
    return lldb::OptionValuePropertiesSP();
}

// This is deprecated way to register plugin specific settings.  e.g.
// "<plugin_type_name>.plugin.<plugin_type_desc>.SETTINGNAME"
// and Platform generic settings would be under "platform.SETTINGNAME".
static lldb::OptionValuePropertiesSP
GetDebuggerPropertyForPluginsOldStyle (Debugger &debugger,
                                       const ConstString &plugin_type_name,
                                       const ConstString &plugin_type_desc,
                                       bool can_create)
{
    static ConstString g_property_name("plugin");
    lldb::OptionValuePropertiesSP parent_properties_sp (debugger.GetValueProperties());
    if (parent_properties_sp)
    {
        OptionValuePropertiesSP plugin_properties_sp = parent_properties_sp->GetSubProperty(nullptr, plugin_type_name);
        if (!plugin_properties_sp && can_create)
        {
            plugin_properties_sp.reset (new OptionValueProperties (plugin_type_name));
            parent_properties_sp->AppendProperty (plugin_type_name,
                                                  plugin_type_desc,
                                                  true,
                                                  plugin_properties_sp);
        }
        
        if (plugin_properties_sp)
        {
            lldb::OptionValuePropertiesSP plugin_type_properties_sp = plugin_properties_sp->GetSubProperty(nullptr, g_property_name);
            if (!plugin_type_properties_sp && can_create)
            {
                plugin_type_properties_sp.reset (new OptionValueProperties (g_property_name));
                plugin_properties_sp->AppendProperty (g_property_name,
                                                      ConstString("Settings specific to plugins"),
                                                      true,
                                                      plugin_type_properties_sp);
            }
            return plugin_type_properties_sp;
        }
    }
    return lldb::OptionValuePropertiesSP();
}

namespace {

typedef lldb::OptionValuePropertiesSP
GetDebuggerPropertyForPluginsPtr (Debugger&, const ConstString&, const ConstString&, bool can_create);

lldb::OptionValuePropertiesSP
GetSettingForPlugin (Debugger &debugger,
                     const ConstString &setting_name,
                     const ConstString &plugin_type_name,
                     GetDebuggerPropertyForPluginsPtr get_debugger_property= GetDebuggerPropertyForPlugins)
{
    lldb::OptionValuePropertiesSP properties_sp;
    lldb::OptionValuePropertiesSP plugin_type_properties_sp (get_debugger_property (debugger,
                                                                                    plugin_type_name,
                                                                                    ConstString(), // not creating to so we don't need the description
                                                                                    false));
    if (plugin_type_properties_sp)
        properties_sp = plugin_type_properties_sp->GetSubProperty (nullptr, setting_name);
    return properties_sp;
}

bool
CreateSettingForPlugin (Debugger &debugger,
                        const ConstString &plugin_type_name,
                        const ConstString &plugin_type_desc,
                        const lldb::OptionValuePropertiesSP &properties_sp,
                        const ConstString &description,
                        bool is_global_property,
                        GetDebuggerPropertyForPluginsPtr get_debugger_property = GetDebuggerPropertyForPlugins)
{
    if (properties_sp)
    {
        lldb::OptionValuePropertiesSP plugin_type_properties_sp (get_debugger_property (
            debugger, plugin_type_name, plugin_type_desc, true));
        if (plugin_type_properties_sp)
        {
            plugin_type_properties_sp->AppendProperty (properties_sp->GetName(),
                                                       description,
                                                       is_global_property,
                                                       properties_sp);
            return true;
        }
    }
    return false;
}

const char* kDynamicLoaderPluginName("dynamic-loader");
const char* kPlatformPluginName("platform");
const char* kProcessPluginName("process");
const char* kSymbolFilePluginName("symbol-file");
const char* kJITLoaderPluginName("jit-loader");

} // anonymous namespace

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForDynamicLoaderPlugin (Debugger &debugger,
                                                 const ConstString &setting_name)
{
    return GetSettingForPlugin(debugger, setting_name, ConstString(kDynamicLoaderPluginName));
}

bool
PluginManager::CreateSettingForDynamicLoaderPlugin (Debugger &debugger,
                                                    const lldb::OptionValuePropertiesSP &properties_sp,
                                                    const ConstString &description,
                                                    bool is_global_property)
{
    return CreateSettingForPlugin(debugger,
                                  ConstString(kDynamicLoaderPluginName),
                                  ConstString("Settings for dynamic loader plug-ins"),
                                  properties_sp,
                                  description,
                                  is_global_property);
}

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForPlatformPlugin (Debugger &debugger, const ConstString &setting_name)
{
    return GetSettingForPlugin(debugger,
                               setting_name,
                               ConstString(kPlatformPluginName),
                               GetDebuggerPropertyForPluginsOldStyle);
}

bool
PluginManager::CreateSettingForPlatformPlugin (Debugger &debugger,
                                               const lldb::OptionValuePropertiesSP &properties_sp,
                                               const ConstString &description,
                                               bool is_global_property)
{
    return CreateSettingForPlugin(debugger,
                                  ConstString(kPlatformPluginName),
                                  ConstString("Settings for platform plug-ins"),
                                  properties_sp,
                                  description,
                                  is_global_property,
                                  GetDebuggerPropertyForPluginsOldStyle);
}

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForProcessPlugin (Debugger &debugger, const ConstString &setting_name)
{
    return GetSettingForPlugin(debugger, setting_name, ConstString(kProcessPluginName));
}

bool
PluginManager::CreateSettingForProcessPlugin (Debugger &debugger,
                                              const lldb::OptionValuePropertiesSP &properties_sp,
                                              const ConstString &description,
                                              bool is_global_property)
{
    return CreateSettingForPlugin(debugger,
                                  ConstString(kProcessPluginName),
                                  ConstString("Settings for process plug-ins"),
                                  properties_sp,
                                  description,
                                  is_global_property);
}

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForSymbolFilePlugin (Debugger &debugger,
                                              const ConstString &setting_name)
{
    return GetSettingForPlugin(debugger, setting_name, ConstString(kSymbolFilePluginName));
}

bool
PluginManager::CreateSettingForSymbolFilePlugin (Debugger &debugger,
                                                 const lldb::OptionValuePropertiesSP &properties_sp,
                                                 const ConstString &description,
                                                 bool is_global_property)
{
    return CreateSettingForPlugin(debugger,
                                  ConstString(kSymbolFilePluginName),
                                  ConstString("Settings for symbol file plug-ins"),
                                  properties_sp,
                                  description,
                                  is_global_property);
}

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForJITLoaderPlugin (Debugger &debugger,
                                             const ConstString &setting_name)
{
    return GetSettingForPlugin(debugger, setting_name, ConstString(kJITLoaderPluginName));
}

bool
PluginManager::CreateSettingForJITLoaderPlugin (Debugger &debugger,
                                                const lldb::OptionValuePropertiesSP &properties_sp,
                                                const ConstString &description,
                                                bool is_global_property)
{
    return CreateSettingForPlugin(debugger,
                                  ConstString(kJITLoaderPluginName),
                                  ConstString("Settings for JIT loader plug-ins"),
                                  properties_sp,
                                  description,
                                  is_global_property);
}

static const char *kOperatingSystemPluginName("os");

lldb::OptionValuePropertiesSP
PluginManager::GetSettingForOperatingSystemPlugin(Debugger &debugger, const ConstString &setting_name)
{
    lldb::OptionValuePropertiesSP properties_sp;
    lldb::OptionValuePropertiesSP plugin_type_properties_sp(
        GetDebuggerPropertyForPlugins(debugger, ConstString(kOperatingSystemPluginName),
                                      ConstString(), // not creating to so we don't need the description
                                      false));
    if (plugin_type_properties_sp)
        properties_sp = plugin_type_properties_sp->GetSubProperty(nullptr, setting_name);
    return properties_sp;
}

bool
PluginManager::CreateSettingForOperatingSystemPlugin(Debugger &debugger,
                                                     const lldb::OptionValuePropertiesSP &properties_sp,
                                                     const ConstString &description, bool is_global_property)
{
    if (properties_sp)
    {
        lldb::OptionValuePropertiesSP plugin_type_properties_sp(
            GetDebuggerPropertyForPlugins(debugger, ConstString(kOperatingSystemPluginName),
                                          ConstString("Settings for operating system plug-ins"), true));
        if (plugin_type_properties_sp)
        {
            plugin_type_properties_sp->AppendProperty(properties_sp->GetName(), description, is_global_property,
                                                      properties_sp);
            return true;
        }
    }
    return false;
}
